#ifndef LOG_H
#define LOG_H

namespace ohns {

enum class LogLevel {
    LEVEL_FATAL,
    LEVEL_ERR,
    LEVEL_WARN,
    LEVEL_INFO,
    LEVEL_DEBUG, // DEBUG is a macro on windows, so use LEVEL_DEBUG instead.
    COUNT,
};

class Log {
  public:
    static LogLevel slogLevel; // for read only

    static inline void setLogLevel(LogLevel level) { slogLevel = level; }
    static void logMessage(LogLevel level, const char *formats, ...);
};

} // namespace ohns

#define CC_LOG_DEBUG(formats, ...)                           \
    if (ohns::Log::slogLevel >= ohns::LogLevel::LEVEL_DEBUG) \
    ohns::Log::logMessage(ohns::LogLevel::LEVEL_DEBUG, formats, ##__VA_ARGS__)
#define CC_LOG_INFO(formats, ...)                     \
    if (ohns::Log::slogLevel >= ohns::LogLevel::LEVEL_INFO) \
    ohns::Log::logMessage(ohns::LogLevel::LEVEL_INFO, formats, ##__VA_ARGS__)
#define CC_LOG_WARNING(formats, ...)                  \
    if (ohns::Log::slogLevel >= ohns::LogLevel::LEVEL_WARN) \
    ohns::Log::logMessage(ohns::LogLevel::LEVEL_WARN, formats, ##__VA_ARGS__)
#define DO_CC_LOG_ERROR(formats, ...)                \
    if (ohns::Log::slogLevel >= ohns::LogLevel::LEVEL_ERR) \
    ohns::Log::logMessage(ohns::LogLevel::LEVEL_ERR, formats, ##__VA_ARGS__)
#define CC_LOG_FATAL(formats, ...)                     \
    if (ohns::Log::slogLevel >= ohns::LogLevel::LEVEL_FATAL) \
    ohns::Log::logMessage(ohns::LogLevel::LEVEL_FATAL, formats, ##__VA_ARGS__)

#define CC_LOG_ERROR(formats, ...)                                        \
    do {                                                                  \
        DO_CC_LOG_ERROR("[ERROR] file %s: line %d ", __FILE__, __LINE__); \
        DO_CC_LOG_ERROR(formats, ##__VA_ARGS__);                          \
    } while (0)

#endif